#library('HTMLEntities');

class HtmlEntities {
  
  static final Map<String,String> unsafeEntities = const {
    @"<" : "&lt;",
    @">" : "&gt;",
    @"&" : "&amp;"
  };
    
  static final Map<String,String> safeEntities = const {
    // from wikipedia: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
    "\"" : "&quot;",
    @"'" : "&apos;",

    //@" " : "&nbsp;",
    @"¡" : "&iexcl;",
    @"¢" : "&cent;",
    @"£" : "&pound;",
    @"¤" : "&curren;",
    @"¥" : "&yen;",
    @"¦" : "&brvbar;",
    @"§" : "&sect;",
    @"¨" : "&uml;",
    @"©" : "&copy;",
    @"ª" : "&ordf;",
    @"«" : "&laquo;",
    @"¬" : "&not;",
    //@" " : "&shy;",
    @"®" : "&reg;",
    @"¯" : "&macr;",
    @"°" : "&deg;",
    @"±" : "&plusmn;",
    @"²" : "&sup2;",
    @"³" : "&sup3;",
    @"´" : "&acute;",
    @"µ" : "&micro;",
    @"¶" : "&para;",
    @"·" : "&middot;",
    @"¸" : "&cedil;",
    @"¹" : "&sup1;",
    @"º" : "&ordm;",
    @"»" : "&raquo;",
    @"¼" : "&frac14;",
    @"½" : "&frac12;",
    @"¾" : "&frac34;",
    @"¿" : "&iquest;",
    @"À" : "&Agrave;",
    @"Á" : "&Aacute;",
    @"Â" : "&Acirc;",
    @"Ã" : "&Atilde;",
    @"Ä" : "&Auml;",
    @"Å" : "&Aring;",
    @"Æ" : "&AElig;",
    @"Ç" : "&Ccedil;",
    @"È" : "&Egrave;",
    @"É" : "&Eacute;",
    @"Ê" : "&Ecirc;",
    @"Ë" : "&Euml;",
    @"Ì" : "&Igrave;",
    @"Í" : "&Iacute;",
    @"Î" : "&Icirc;",
    @"Ï" : "&Iuml;",
    @"Ð" : "&ETH;",
    @"Ñ" : "&Ntilde;",
    @"Ò" : "&Ograve;",
    @"Ó" : "&Oacute;",
    @"Ô" : "&Ocirc;",
    @"Õ" : "&Otilde;",
    @"Ö" : "&Ouml;",
    @"×" : "&times;",
    @"Ø" : "&Oslash;",
    @"Ù" : "&Ugrave;",
    @"Ú" : "&Uacute;",
    @"Û" : "&Ucirc;",
    @"Ü" : "&Uuml;",
    @"Ý" : "&Yacute;",
    @"Þ" : "&THORN;",
    @"ß" : "&szlig;",
    @"à" : "&agrave;",
    @"á" : "&aacute;",
    @"â" : "&acirc;",
    @"ã" : "&atilde;",
    @"ä" : "&auml;",
    @"å" : "&aring;",
    @"æ" : "&aelig;",
    @"ç" : "&ccedil;",
    @"è" : "&egrave;",
    @"é" : "&eacute;",
    @"ê" : "&ecirc;",
    @"ë" : "&euml;",
    @"ì" : "&igrave;",
    @"í" : "&iacute;",
    @"î" : "&icirc;",
    @"ï" : "&iuml;",
    @"ð" : "&eth;",
    @"ñ" : "&ntilde;",
    @"ò" : "&ograve;",
    @"ó" : "&oacute;",
    @"ô" : "&ocirc;",
    @"õ" : "&otilde;",
    @"ö" : "&ouml;",
    @"÷" : "&divide;",
    @"ø" : "&oslash;",
    @"ù" : "&ugrave;",
    @"ú" : "&uacute;",
    @"û" : "&ucirc;",
    @"ü" : "&uuml;",
    @"ý" : "&yacute;",
    @"þ" : "&thorn;",
    @"ÿ" : "&yuml;",
    @"Œ" : "&OElig;",
    @"œ" : "&oelig;",
    @"Š" : "&Scaron;",
    @"š" : "&scaron;",
    @"Ÿ" : "&Yuml;",
    @"ƒ" : "&fnof;",
    @"ˆ" : "&circ;",
    @"˜" : "&tilde;",
    @"Α" : "&Alpha;",
    @"Β" : "&Beta;",
    @"Γ" : "&Gamma;",
    @"Δ" : "&Delta;",
    @"Ε" : "&Epsilon;",
    @"Ζ" : "&Zeta;",
    @"Η" : "&Eta;",
    @"Θ" : "&Theta;",
    @"Ι" : "&Iota;",
    @"Κ" : "&Kappa;",
    @"Λ" : "&Lambda;",
    @"Μ" : "&Mu;",
    @"Ν" : "&Nu;",
    @"Ξ" : "&Xi;",
    @"Ο" : "&Omicron;",
    @"Π" : "&Pi;",
    @"Ρ" : "&Rho;",
    @"Σ" : "&Sigma;",
    @"Τ" : "&Tau;",
    @"Υ" : "&Upsilon;",
    @"Φ" : "&Phi;",
    @"Χ" : "&Chi;",
    @"Ψ" : "&Psi;",
    @"Ω" : "&Omega;",
    @"α" : "&alpha;",
    @"β" : "&beta;",
    @"γ" : "&gamma;",
    @"δ" : "&delta;",
    @"ε" : "&epsilon;",
    @"ζ" : "&zeta;",
    @"η" : "&eta;",
    @"θ" : "&theta;",
    @"ι" : "&iota;",
    @"κ" : "&kappa;",
    @"λ" : "&lambda;",
    @"μ" : "&mu;",
    @"ν" : "&nu;",
    @"ξ" : "&xi;",
    @"ο" : "&omicron;",
    @"π" : "&pi;",
    @"ρ" : "&rho;",
    @"ς" : "&sigmaf;",
    @"σ" : "&sigma;",
    @"τ" : "&tau;",
    @"υ" : "&upsilon;",
    @"φ" : "&phi;",
    @"χ" : "&chi;",
    @"ψ" : "&psi;",
    @"ω" : "&omega;",
    @"ϑ" : "&thetasym;",
    @"ϒ" : "&upsih;",
    @"ϖ" : "&piv;",
    //@" " : "&ensp;",
    //@" " : "&emsp;",
    //@" " : "&thinsp;",
    //@" " : "&zwnj;",
    //@" " : "&zwj;",
    //@" " : "&lrm;",
    //@" " : "&rlm;",
    @"–" : "&ndash;",
    @"—" : "&mdash;",
    @"‘" : "&lsquo;",
    @"’" : "&rsquo;",
    @"‚" : "&sbquo;",
    @"“" : "&ldquo;",
    @"”" : "&rdquo;",
    @"„" : "&bdquo;",
    @"†" : "&dagger;",
    @"‡" : "&Dagger;",
    @"•" : "&bull;",
    @"…" : "&hellip;",
    @"‰" : "&permil;",
    @"′" : "&prime;",
    @"″" : "&Prime;",
    @"‹" : "&lsaquo;",
    @"›" : "&rsaquo;",
    @"‾" : "&oline;",
    @"⁄" : "&frasl;",
    @"€" : "&euro;",
    @"ℑ" : "&image;",
    @"℘" : "&weierp;",
    @"ℜ" : "&real;",
    @"™" : "&trade;",
    @"ℵ" : "&alefsym;",
    @"←" : "&larr;",
    @"↑" : "&uarr;",
    @"→" : "&rarr;",
    @"↓" : "&darr;",
    @"↔" : "&harr;",
    @"↵" : "&crarr;",
    @"⇐" : "&lArr;",
    @"⇑" : "&uArr;",
    @"⇒" : "&rArr;",
    @"⇓" : "&dArr;",
    @"⇔" : "&hArr;",
    @"∀" : "&forall;",
    @"∂" : "&part;",
    @"∃" : "&exist;",
    @"∅" : "&empty;",
    @"∇" : "&nabla;",
    @"∈" : "&isin;",
    @"∉" : "&notin;",
    @"∋" : "&ni;",
    @"∏" : "&prod;",
    @"∑" : "&sum;",
    @"−" : "&minus;",
    @"∗" : "&lowast;",
    @"√" : "&radic;",
    @"∝" : "&prop;",
    @"∞" : "&infin;",
    @"∠" : "&ang;",
    @"∧" : "&and;",
    @"∨" : "&or;",
    @"∩" : "&cap;",
    @"∪" : "&cup;",
    @"∫" : "&int;",
    @"∴" : "&there4;",
    @"∼" : "&sim;",
    @"≅" : "&cong;",
    @"≈" : "&asymp;",
    @"≠" : "&ne;",
    @"≡" : "&equiv;",
    @"≤" : "&le;",
    @"≥" : "&ge;",
    @"⊂" : "&sub;",
    @"⊃" : "&sup;",
    @"⊄" : "&nsub;",
    @"⊆" : "&sube;",
    @"⊇" : "&supe;",
    @"⊕" : "&oplus;",
    @"⊗" : "&otimes;",
    @"⊥" : "&perp;",
    @"⋅" : "&sdot;",
    @"⌈" : "&lceil;",
    @"⌉" : "&rceil;",
    @"⌊" : "&lfloor;",
    @"⌋" : "&rfloor;",
    @"〈" : "&lang;",
    @"〉" : "&rang;",
    @"◊" : "&loz;",
    @"♠" : "&spades;",
    @"♣" : "&clubs;",
    @"♥" : "&hearts;",
    @"♦" : "&diams;",

    // http://tlt.its.psu.edu/suggestions/international/bylanguage/czechslovak.html#htmlcodes
    // CZECH
    @"Č" : "&#268;",
    @"č" : "&#269;",
    @"Ď" : "&#270;",
    @"ď" : "&#271;",
    @"Ľ" : "&#317;",
    @"ľ" : "&#318;",
    @"Ň" : "&#327;",
    @"ň" : "&#328;",
    @"Ř" : "&#344;",
    @"ř" : "&#345;",
//    @"Š" : "&#352;",
//    @"š" : "&#353;",
    @"Ť" : "&#356;",
    @"ť" : "&#357;",
    @"Ž" : "&#381;",
    @"ž" : "&#382;",
    @"Ů" : "&#366;",
    @"ů" : "&#367;",
//    @"Ä" : "&Auml;",
//    @"ä" : "&auml;",
    @"Ě" : "&#282;",
    @"ě" : "&#283;",
//    @"Ô" : "&Ocirc;",
//    @"ô" : "&ocirc;",
    @"Ĺ" : "&#313;",
    @"ĺ" : "&#314;",
    @"Ŕ" : "&#340;",
    @"ŕ" : "&#341;"
  };
  
  static final Map<String,String> safeEntitiesSubset = const {
    // from wikipedia: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
    "\"" : "&quot;",
    @"'" : "&apos;",

    @"À" : "&Agrave;",
    @"Á" : "&Aacute;",
    @"Â" : "&Acirc;",
    @"Ã" : "&Atilde;",
    @"Ä" : "&Auml;",
    @"Å" : "&Aring;",
    @"Æ" : "&AElig;",
    @"Ç" : "&Ccedil;",
    @"È" : "&Egrave;",
    @"É" : "&Eacute;",
    @"Ê" : "&Ecirc;",
    @"Ë" : "&Euml;",
    @"Ì" : "&Igrave;",
    @"Í" : "&Iacute;",
    @"Î" : "&Icirc;",
    @"Ï" : "&Iuml;",
    @"Ð" : "&ETH;",
    @"Ñ" : "&Ntilde;",
    @"Ò" : "&Ograve;",
    @"Ó" : "&Oacute;",
    @"Ô" : "&Ocirc;",
    @"Õ" : "&Otilde;",
    @"Ö" : "&Ouml;",
    @"×" : "&times;",
    @"Ø" : "&Oslash;",
    @"Ù" : "&Ugrave;",
    @"Ú" : "&Uacute;",
    @"Û" : "&Ucirc;",
    @"Ü" : "&Uuml;",
    @"Ý" : "&Yacute;",
    @"Þ" : "&THORN;",
    @"ß" : "&szlig;",
    @"à" : "&agrave;",
    @"á" : "&aacute;",
    @"â" : "&acirc;",
    @"ã" : "&atilde;",
    @"ä" : "&auml;",
    @"å" : "&aring;",
    @"æ" : "&aelig;",
    @"ç" : "&ccedil;",
    @"è" : "&egrave;",
    @"é" : "&eacute;",
    @"ê" : "&ecirc;",
    @"ë" : "&euml;",
    @"ì" : "&igrave;",
    @"í" : "&iacute;",
    @"î" : "&icirc;",
    @"ï" : "&iuml;",
    @"ð" : "&eth;",
    @"ñ" : "&ntilde;",
    @"ò" : "&ograve;",
    @"ó" : "&oacute;",
    @"ô" : "&ocirc;",
    @"õ" : "&otilde;",
    @"ö" : "&ouml;",
    @"÷" : "&divide;",
    @"ø" : "&oslash;",
    @"ù" : "&ugrave;",
    @"ú" : "&uacute;",
    @"û" : "&ucirc;",
    @"ü" : "&uuml;",
    @"ý" : "&yacute;",
    @"þ" : "&thorn;",
    @"ÿ" : "&yuml;",
    @"Œ" : "&OElig;",
    @"œ" : "&oelig;",
    @"Š" : "&Scaron;",
    @"š" : "&scaron;",
    @"Ÿ" : "&Yuml;",
    @"ƒ" : "&fnof;",

    @"—" : "&mdash;",
    @"‘" : "&lsquo;",
    @"’" : "&rsquo;",
    @"‚" : "&sbquo;",
    @"“" : "&ldquo;",
    @"”" : "&rdquo;",
    @"„" : "&bdquo;",

    @"…" : "&hellip;",

    @"′" : "&prime;",
    @"″" : "&Prime;",
    @"‹" : "&lsaquo;",
    @"›" : "&rsaquo;",

    @"€" : "&euro;",

    @"™" : "&trade;",

    @"←" : "&larr;",
    @"↑" : "&uarr;",
    @"→" : "&rarr;",
    @"↓" : "&darr;",
    @"↔" : "&harr;",
    @"↵" : "&crarr;",
    @"⇐" : "&lArr;",
    @"⇑" : "&uArr;",
    @"⇒" : "&rArr;",
    @"⇓" : "&dArr;",
    @"⇔" : "&hArr;",

    // CZECH
    @"Č" : "&#268;",
    @"č" : "&#269;",
    @"Ď" : "&#270;",
    @"ď" : "&#271;",
    @"Ľ" : "&#317;",
    @"ľ" : "&#318;",
    @"Ň" : "&#327;",
    @"ň" : "&#328;",
    @"Ř" : "&#344;",
    @"ř" : "&#345;",
//    @"Š" : "&#352;",
//    @"š" : "&#353;",
    @"Ť" : "&#356;",
    @"ť" : "&#357;",
    @"Ž" : "&#381;",
    @"ž" : "&#382;",
    @"Ů" : "&#366;",
    @"ů" : "&#367;",
//    @"Ä" : "&Auml;",
//    @"ä" : "&auml;",
    @"Ě" : "&#282;",
    @"ě" : "&#283;",
//    @"Ô" : "&Ocirc;",
//    @"ô" : "&ocirc;",
    @"Ĺ" : "&#313;",
    @"ĺ" : "&#314;",
    @"Ŕ" : "&#340;",
    @"ŕ" : "&#341;"

  };
  
  static final Map<String,String> safeEntitiesToCharsSubset = const {
    // from wikipedia: http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
    "\"" : "&quot;",
    "&apos;" : @"'",
    
    "&Agrave;" : @"À",
    "&Aacute;" : @"Á",
    "&Acirc;" : @"Â",
    "&Atilde;" : @"Ã",
    "&Auml;" : @"Ä",
    "&Aring;" : @"Å",
    "&AElig;" : @"Æ",
    "&Ccedil;" : @"Ç",
    "&Egrave;" : @"È",
    "&Eacute;" : @"É",
    "&Ecirc;" : @"Ê",
    "&Euml;" : @"Ë",
    "&Igrave;" : @"Ì",
    "&Iacute;" : @"Í",
    "&Icirc;" : @"Î",
    "&Iuml;" : @"Ï",
    "&ETH;" : @"Ð",
    "&Ntilde;" : @"Ñ",
    "&Ograve;" : @"Ò",
    "&Oacute;" : @"Ó",
    "&Ocirc;" : @"Ô",
    "&Otilde;" : @"Õ",
    "&Ouml;" : @"Ö",
    "&times;" : @"×",
    "&Oslash;" : @"Ø",
    "&Ugrave;" : @"Ù",
    "&Uacute;" : @"Ú",
    "&Ucirc;" : @"Û",
    "&Uuml;" : @"Ü",
    "&Yacute;" : @"Ý",
    "&THORN;" : @"Þ",
    "&szlig;" : @"ß",
    "&agrave;" : @"à",
    "&aacute;" : @"á",
    "&acirc;" : @"â",
    "&atilde;" : @"ã",
    "&auml;" : @"ä",
    "&aring;" : @"å",
    "&aelig;" : @"æ",
    "&ccedil;" : @"ç",
    "&egrave;" : @"è",
    "&eacute;" : @"é",
    "&ecirc;" : @"ê",
    "&euml;" : @"ë",
    "&igrave;" : @"ì",
    "&iacute;" : @"í",
    "&icirc;" : @"î",
    "&iuml;" : @"ï",
    "&eth;" : @"ð",
    "&ntilde;" : @"ñ",
    "&ograve;" : @"ò",
    "&oacute;" : @"ó",
    "&ocirc;" : @"ô",
    "&otilde;" : @"õ",
    "&ouml;" : @"ö",
    "&divide;" : @"÷",
    "&oslash;" : @"ø",
    "&ugrave;" : @"ù",
    "&uacute;" : @"ú",
    "&ucirc;" : @"û",
    "&uuml;" : @"ü",
    "&yacute;" : @"ý",
    "&thorn;" : @"þ",
    "&yuml;" : @"ÿ",
    "&OElig;" : @"Œ",
    "&oelig;" : @"œ",
    "&Scaron;" : @"Š",
    "&scaron;" : @"š",
    "&Yuml;" : @"Ÿ",
    "&fnof;" : @"ƒ",
    
    "&mdash;" : @"—",
    "&lsquo;" : @"‘",
    "&rsquo;" : @"’",
    "&sbquo;" : @"‚",
    "&ldquo;" : @"“",
    "&rdquo;" : @"”",
    "&bdquo;" : @"„",
    
    "&hellip;" : @"…",
    
    "&prime;" : @"′",
    "&Prime;" : @"″",
    "&lsaquo;" : @"‹",
    "&rsaquo;" : @"›",
    
    "&euro;" : @"€",
    
    "&trade;" : @"™",
    
    "&larr;" : @"←",
    "&uarr;" : @"↑",
    "&rarr;" : @"→",
    "&darr;" : @"↓",
    "&harr;" : @"↔",
    "&crarr;" : @"↵",
    "&lArr;" : @"⇐",
    "&uArr;" : @"⇑",
    "&rArr;" : @"⇒",
    "&dArr;" : @"⇓",
    "&hArr;" : @"⇔",
    
    // CZECH
    "&#268;" : @"Č",
    "&#269;" : @"č",
    "&#270;" : @"Ď",
    "&#271;" : @"ď",
    "&#317;" : @"Ľ",
    "&#318;" : @"ľ",
    "&#327;" : @"Ň",
    "&#328;" : @"ň",
    "&#344;" : @"Ř",
    "&#345;" : @"ř",
    //"&#352;" : @"Š",
    //"&#353;" : @"š",
    "&#356;" : @"Ť",
    "&#357;" : @"ť",
    "&#381;" : @"Ž",
    "&#382;" : @"ž",
    "&#366;" : @"Ů",
    "&#367;" : @"ů",
    //"&Auml;" : @"Ä",
    //"&auml;" : @"ä",
    "&#282;" : @"Ě",
    "&#283;" : @"ě",
    //"&Ocirc;" : @"Ô",
    //"&ocirc;" : @"ô",
    "&#313;" : @"Ĺ",
    "&#314;" : @"ĺ",
    "&#340;" : @"Ŕ",
    "&#341;" : @"ŕ"
  };
  
  

  static String toHtml(String s) {
    unsafeEntities.forEach((charUtf, entity) {
        s = s.replaceAll(charUtf, entity);
    });    
    safeEntities.forEach((charUtf, entity) {
        s = s.replaceAll(charUtf, entity);
    });
    return s;
  }
  
  static final RegExp _htmlEntity = const RegExp(@"&(\w{2,8}|#\d{2,4});");
  
  static String collapseSafeEntities(String input, [bool useAll = true]) {
//    var entities = (useAll ? safeEntities : safeEntitiesSubset);
//    safeEntities.forEach((charUtf, entity) {
//      s = s.replaceAll(entity, charUtf);
//    });
    
    StringBuffer strBuf = new StringBuffer();
    int index = 0;

    // find <span class="c2">...</span> and convert to <strong>...</strong>
    Match m = _htmlEntity.firstMatch(input);
    while (m != null) {
      strBuf.add(input.substring(index,index + m.start()));

      if (safeEntitiesToCharsSubset.containsKey(m.group(0))) {
        strBuf.add(safeEntitiesToCharsSubset[m.group(0)]);
      } else {
        strBuf.add(m.group(0));
      }

      index = index + m.end();
      m = _htmlEntity.firstMatch(input.substring(index));
    }
    
    // add the rest
    strBuf.add(input.substring(index));
    
    return strBuf.toString();
  }
}
